<?php

class StdLibProbStatFunctions implements CliLibrary
{
	private static $functions = array(
		'binomdist' => 'f_binom',
		'combin' => 'f_combin',
		'poisson' => 'f_poisson',
	);

	public static function addLibrary (CliMode $mode)
	{
		$mode->addFunctions(__CLASS__, self::$functions);
	}

	public static function f_binom (Argcontext $args)
	{
		$args->requireTypes('number', 'number', 'number');
		$k = $args->toNumber(0);
		$n = $args->toNumber(1);
		$p = $args->toNumber(2);

		$nk = $args->call('combin', array($n, $k));

		return $nk * pow($p, $k) * pow(1 - $p, $n - $k);
	}

	public static function f_combin (ArgContext $args)
	{
		$args->requireTypes('number', 'number');
		$n = (int) $args->toNumber(0);
		$k = (int) $args->toNumber(1);

		if ($n < 0 || $k < 0)
		{
			throw new CliArgumentException(
				"Factorial is only defined for nonnegative integers");
		}

		if ($k > $n)
		{
			throw new CliArgumentException(
				"Combinations can not have more numbers than what's available");
		}

		// Optimized the operation "n! / (k! * (n - k)!" for larger numbers
		$max = max($k, $n - $k);
		$count = $n;

		// Perform the factorial calculation only as far as needed
		for ($sum = 1; $count > $max; $sum *= $count--);

		$div = $args->call('!', array(min($k, $n - $k)));

		return $sum / $div;
	}

	public static function f_poisson (Argcontext $args)
	{
		$args->requireTypes('number', 'number');
		$k = $args->toNumber(0);
		$u = $args->toNumber(1);
		return pow($u, $k) * pow(M_E, -$u) / $args->call('!', array($k));
	}
}